5-7月的实习,总的来说主要做了三件事情,一是语料的补充,具体表现是通过编写分布式爬虫从各种渠道爬取相关语料,二是特征提取,这一阶段测试了各种模型,doc2vec, lda, LSI, RNN, CNN 等等,试图在 word2vec 词向量基础上,产生质量更高的 sentence embedding,这也是本篇的重点所在。三是新问题发现,主要是通过聚类算法的实验实现。难点在于用什么语料聚类以及如何产生自动化标签。其他的工作也就是打打杂,处理、过滤、验证各种数据,产生训练集、测试集,以及做各种 demo 界面,比较简单。
最终还是发现 doc2vec, lda 产生的 sentence embedding 质量太低,在充足并相关的语料下,用 word2vec 得到的词向量效果还是很不错的,sentence embedding 最终的产生还是通过 RNN。这篇重点是 CNN,因为这是我自己负责的,之后会上一篇 RNN 的版本。
CNN
基本介绍见 卷积神经网络 CNN 笔记
来源:Kim Y. Convolutional neural networks for sentence classification[J]. arXiv preprint arXiv:1408.5882, 2014.(http://www.aclweb.org/anthology/D14-1181)
达到了 94.5% 的准确率,并不如 RNN,因为 CNN 模型的 focus 通常是长文本而不是短句,这里的情景是短句 FAQ,这也是效果不如 RNN 的一个原因。
CNN 一些入门介绍见 CNN 及 TensorFlow 实战 MINST
Why CNN
句子可以切分为很多词,词和词组合之后会产生局部语意,句子可以分成若干个有『局部语意』的小块。nlp 里面一个很重要的矛盾就是粒度和语意的矛盾。如果粒度过大,则太稀疏没法玩,粒度过小则意思就不对了。CNN 通过卷积,把每 k 个词组合之后的语意放在一起,得到更为准确的句向量。
实现
数据格式
论文提出的输入类型有以下四种:
- CNN-rand: 所有的 word vector 都是随机初始化的,同时当做训练过程中优化的参数;
- CNN-static: 所有的 word vector 直接使用 Word2Vec 工具得到的结果,并且是固定不变的;
- CNN-non-static: 所有的 word vector 直接使用 Word2Vec 工具得到的结果,这些 word vector 也当做是可优化的参数,在训练过程中被 Fine tuned;
- CNN-multichannel: CNN-static 和 CNN-non-static 的混合版本,即两种类型的输入;
一般来说 non-static vector 要优于 static vector。我们实验用的是随机初始化方法,数据 format:
用户问题\t标准问题id
模型
输入层
输入层是句子中的词语对应的 word vector 依次(从上到下)排列的矩阵,句子有 n 个词,vector的维数为 k ,矩阵就是 n×k。
第一层卷积层
输入层通过卷积操作得到若干个 Feature Map,卷积窗口的大小为 h×k ,其中 h 表示纵向词语的个数,而 k 表示 word vector 的维数。如果 h=2,就是相邻的两个word做一次卷积。通过这样一个大型的卷积窗口,得到若干个列数为 1 的Feature Map。
卷积之后的结果经过激活函数 f 得到 feature,记为$c_i$。它是由$x_{i:i+h−1}$相邻的 h 个 words 卷积得到的值,再 activation 之后的值,也是当前层的输出。
卷积之后的值:$w⋅x_{i:i+h−1}+b$
输出的 feature 值 $c_i=f(w⋅x_{i:i+h−1}+b)$,这就是我们的 sentence embedding
窗口大小:h
这样之后,一个 n 长度的sentence就有$[x_{1:h}, x_{2:h+1},x_{3:h+2},…,x_{n−h+1:n}]$这些 word windows,卷积后的结果就是 c = $[c1,c2,…,c_{n−h+1}]$,维度为(1,n-h+1)
然后进行池化 max pooling,选出最重要的 feature。pooling scheme可以根据句子的长度来选择。
池化层
接下来的池化层,才用 Max-over-time Pooling 的方法。从之前一维的 Feature Map 中提出最大的值,论文中解释最大值代表着最重要的信号。可以看出,这种Pooling方式可以解决可变长度的句子输入问题(因为不管Feature Map中有多少个值,只需要提取其中的最大值)。
最终池化层的输出为各个Feature Map的最大值们,即一个一维的向量。
全连接 + Softmax层
池化层的一维向量的输出通过全连接的方式,连接一个Softmax层,Softmax层可根据任务的需要设置(通常反映着最终类别上的概率分布)。
调参建议
对 Ye Zhang 等人基于 Kim Y 的模型做了大量的调参实验之后的结论:
- 由于模型训练过程中的随机性因素,如随机初始化的权重参数,mini-batch,随机梯度下降优化算法等,造成模型在数据集上的结果有一定的浮动,如准确率(accuracy)能达到 1.5% 的浮动,而AUC 则有 3.4% 的浮动;
- 词向量是使用 word2vec 还是 GloVe,对实验结果有一定的影响,具体哪个更好依赖于任务本身;
- Filter的大小对模型性能有较大的影响,并且Filter的参数应该是可以更新的;
- Feature Map的数量也有一定影响,但是需要兼顾模型的训练效率;
- 1-max pooling的方式已经足够好了,相比于其他的pooling方式而言;
- dropout 非常重要,能够带来 2-4% 的效果提升
- multichannel 的效果没有预期的好
调参建议如下:
- 使用non-static版本的 word2vec 或者 GloVe 要比单纯的 one-hot representation 取得的效果好得多;
- 为了找到最优的过滤器(Filter)大小,可以使用线性搜索的方法。通常过滤器的大小范围在1-10之间,当然对于长句,使用更大的过滤器也是有必要的;
- Feature Map的数量在100-600之间;
- 可以尽量多尝试激活函数,实验发现 ReLU 和 tanh 两种激活函数表现较佳;
- 使用简单的 1-max pooling 就已经足够了,可以没必要设置太复杂的 pooling 方式;
- 当发现增加 Feature Map 的数量使得模型的性能下降时,可以考虑增大正则的力度,如调高dropout的概率;
- 为了检验模型的性能水平,多次反复的交叉验证是必要的,这可以确保模型的高性能并不是偶然。
需要确定的参数:
- input word vector representations;
- filter region size(s);
- the number of feature maps;
- the activation function(s);
- the pooling strategy;
- regularization terms (dropout/l2).
激活函数
tanh
目标函数
交叉熵
遗留问题
还想着能不能用字向量取代词向量,一可以避免分词的麻烦,二可以解决未登录词的问题,这样在测试的时候就很少会遇到Unknown的字向量的问题。另外由于卷积的作用,字向量效果并不一定比词向量差。之后有时间做实验后会更新。
其他实验
LDA
LDA 最大的特点是需要大量的语料,否则数据维度远大于样本数,效果会很差。另外,LDA 适合比较高层次的主题,对稍微细一点的粒度,效果可能就没那么好了。
Doc2vec
能产生很好的词向量,却不能产生很好的句向量。推测原因是句子太短。